/******************************************************************************\
    ContestProblemDataBase.h discribes a database of ties of contests and
	 problems.
    It's used with acm server. See main.cpp for details.
    Copyright (C) 2008  Victor Vinogradov (fly3333@gmail.com)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
\******************************************************************************/

#pragma once

#include ".\BaseDataBase.hpp"

namespace nsContestProblemDataBase
{
	const size_t MAX_LETTER_LENGTH = 15;

	struct sContestProblemData0
	{
		unsigned long Key;
		unsigned long ContestId;
		unsigned long ProblemId;
		char Letter[ MAX_LETTER_LENGTH + 1 ];
	};

	struct sContestProblemData1
	{
		unsigned long Key;
		unsigned long ContestId;
		unsigned long ProblemId;
		char Letter[ MAX_LETTER_LENGTH + 1 ];
	};

	typedef sContestProblemData1 sContestProblemData;

	void ConversionFunction0( void *new_data, const void *prev_data );

	const sConversionTable ConversionTable[] =
	{
		{ sizeof( sContestProblemData0 ), sizeof( sContestProblemData1 ), &ConversionFunction0 }
	};

	struct ContestIdCompare : public std::binary_function< cDataPointer< sContestProblemData >, cDataPointer< sContestProblemData >, bool >
	{
		bool operator()( const cDataPointer< sContestProblemData > &left, const cDataPointer< sContestProblemData > &right ) const
		{
			return left->ContestId < right->ContestId ||
				    left->ContestId == right->ContestId && 
			       ( strlen( left->Letter ) < strlen( right->Letter ) ||
			         strlen( left->Letter ) == strlen( right->Letter ) && _stricmp( left->Letter, right->Letter ) < 0 );
		}
	};

	struct ProblemIdCompare : public std::binary_function< cDataPointer< sContestProblemData >, cDataPointer< sContestProblemData >, bool >
	{
		bool operator()( const cDataPointer< sContestProblemData > &left, const cDataPointer< sContestProblemData > &right ) const
		{
			return left->ProblemId < right->ProblemId ||
				left->ProblemId == right->ProblemId && left->ContestId < right->ContestId;
		}
	};

	typedef cBaseDataBase< sContestProblemData >::KeyIterator KeyIterator;
	typedef cPublicIterator< sContestProblemData, ContestIdCompare > ContestIdIterator;
	typedef cPublicIterator< sContestProblemData, ProblemIdCompare > ProblemIdIterator;

	class cWardContestProblemDataBase : public cBaseDataBase< sContestProblemData >
	{
	private:
		std::set< cDataPointer< sContestProblemData >, ContestIdCompare > ContestIdIndex;
		std::set< cDataPointer< sContestProblemData >, ProblemIdCompare > ProblemIdIndex;
	protected:
		virtual bool Indexed( const cDataPointer< sContestProblemData > &data_pointer )
		{
			return ContestIdIndex.find( data_pointer ) != ContestIdIndex.end() ||
				    ProblemIdIndex.find( data_pointer ) != ProblemIdIndex.end();
		}
		virtual void InsertIndex( const cDataPointer< sContestProblemData > &data_pointer )
		{
			ContestIdIndex.insert( data_pointer );
			ProblemIdIndex.insert( data_pointer );
		}
		virtual void UpdateIndex( const cDataPointer< sContestProblemData > &data_pointer, unsigned long index ) 
		{
			ContestIdIndex.find( data_pointer )->Index = index;
			ProblemIdIndex.find( data_pointer )->Index = index;
		}
		virtual void DeleteIndex( const cDataPointer< sContestProblemData > &data_pointer )
		{
			ContestIdIndex.erase( data_pointer );
			ProblemIdIndex.erase( data_pointer );
		}
		virtual void ClearIndex()
		{
			ContestIdIndex.clear();
			ProblemIdIndex.clear();
		}
	public:
		cWardContestProblemDataBase( /*bool auto_update*/ )
			: cBaseDataBase( /*auto_update*/ )
		{
		}
		ContestIdIterator BeginByContestId() const
		{
			return ContestIdIterator( ContestIdIndex.begin() );
		}
		ContestIdIterator LowerBoundByContestId( unsigned long contest_id ) const
		{
			sContestProblemData contest_problem_data;
			contest_problem_data.ContestId = contest_id;
			strcpy( contest_problem_data.Letter, "" );
			return ContestIdIterator( ContestIdIndex.lower_bound( cDataPointer< sContestProblemData >( &contest_problem_data ) ) );
		}
		ContestIdIterator UpperBoundByContestId( unsigned long contest_id ) const
		{
			sContestProblemData contest_problem_data;
			contest_problem_data.ContestId = contest_id;
			memset( contest_problem_data.Letter, ( unsigned char )'0xff', MAX_LETTER_LENGTH );
			contest_problem_data.Letter[ MAX_LETTER_LENGTH ] = '\0';
			return ContestIdIterator( ContestIdIndex.upper_bound( cDataPointer< sContestProblemData >( &contest_problem_data ) ) );
		}
		ContestIdIterator EndByContestId() const
		{
			return ContestIdIterator( ContestIdIndex.end() );
		}
		ContestIdIterator FindByContestIdAndLetter( unsigned long contest_id, const char letter[] )
		{
			sContestProblemData contest_problem_data;
			contest_problem_data.ContestId = contest_id;
			strncpy( contest_problem_data.Letter, letter, MAX_LETTER_LENGTH )[ MAX_LETTER_LENGTH ] = '\0';
			return ContestIdIterator( ContestIdIndex.find( cDataPointer< sContestProblemData >( &contest_problem_data ) ) );
		}
		bool GetByContestIdAndLetter( unsigned long contest_id, const char letter[], sContestProblemData &contest_problem_data )
		{
			cLocker locker( *this );
			ContestIdIterator iterator = FindByContestIdAndLetter( contest_id, letter );
			if( iterator == EndByContestId() )
				return false;
			contest_problem_data = *iterator;
			return true;
		}
		ProblemIdIterator BeginByProblemId() const
		{
			return ProblemIdIterator( ProblemIdIndex.begin() );
		}
		ProblemIdIterator LowerBoundByProblemId( unsigned long problem_id ) const
		{
			sContestProblemData contest_problem_data;
			contest_problem_data.ProblemId = problem_id;
			contest_problem_data.ContestId = 0ul;
			return ProblemIdIterator( ProblemIdIndex.lower_bound( cDataPointer< sContestProblemData >( &contest_problem_data ) ) );
		}
		ProblemIdIterator UpperBoundByProblemId( unsigned long problem_id ) const
		{
			sContestProblemData contest_problem_data;
			contest_problem_data.ProblemId = problem_id;
			contest_problem_data.ContestId = 0xfffffffful;
			return ProblemIdIterator( ProblemIdIndex.upper_bound( cDataPointer< sContestProblemData >( &contest_problem_data ) ) );
		}
		ProblemIdIterator EndByProblemId() const
		{
			return ProblemIdIterator( ProblemIdIndex.end() );
		}
	};

	class cContestProblemDataBase : public cPointerObject< cWardContestProblemDataBase >
	{
	public:
		bool Create( /*bool auto_update = false*/ )
		{
			cWardContestProblemDataBase *ward_contest_problem_data_base;
			try
			{
				ward_contest_problem_data_base = new cWardContestProblemDataBase( /*auto_update*/ );
			}
			catch( ... )
			{
				return false;
			}
			if( !SwitchToWardObject( ward_contest_problem_data_base ) )
			{
				delete ward_contest_problem_data_base;
				return false;
			}
			return true;
		}
		void Destroy()
		{
			SwitchToSubObject( NULL );
		}
	};
}
